package com.zndroid.daemon;

import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.zndroid.daemon.core.AbsWorkService;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

public final class DaemonX {

    private DaemonX() {}

    private static boolean isDebug;

    public static final int DEFAULT_WAKE_UP_INTERVAL = 6 * 60 * 1000;
    private static final int MINIMAL_WAKE_UP_INTERVAL = 3 * 60 * 1000;

    public static WeakReference<Context> sApp;
    public static Class<? extends AbsWorkService> sServiceClass;
    public static boolean sInitialized;

    private static int sWakeUpInterval = DEFAULT_WAKE_UP_INTERVAL;
    private static final Map<Class<? extends Service>, ServiceConnection> BIND_STATE_MAP = new HashMap<>();

    private static AbsWorkService workService;

    public static Intent intent;
    public static int flags, startId;

    public static void setDebug(boolean isDebug) {
        DaemonX.isDebug = isDebug;
    }

    public static boolean isDebug() {
        return isDebug;
    }

    /**
     * call at Application onCreate() first.
     * @param app Application Context.
     * @param wakeUpInterval 定时唤醒的时间间隔(ms).
     */
    public static void initialize(@NonNull Context app, @NonNull Class<? extends AbsWorkService> serviceClass, @Nullable Integer wakeUpInterval) {
        sApp = new WeakReference<>(app);
        sServiceClass = serviceClass;
        try {
            workService = sServiceClass.newInstance();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

        if (wakeUpInterval != null) sWakeUpInterval = wakeUpInterval;
        sInitialized = true;
    }

    public static void startDaemon(@NonNull final Class<? extends Service> serviceClass) {
        if (!sInitialized) return;
        final Intent i = new Intent(sApp.get(), serviceClass);
        startServiceSafely(i);
        ServiceConnection bound = BIND_STATE_MAP.get(serviceClass);
        if (bound == null) sApp.get().bindService(i, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                BIND_STATE_MAP.put(serviceClass, this);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                BIND_STATE_MAP.remove(serviceClass);
                startServiceSafely(i);
                if (!sInitialized) return;
                sApp.get().bindService(i, this, Context.BIND_AUTO_CREATE);
            }
            
            @Override
            public void onBindingDied(ComponentName name) {
                onServiceDisconnected(name);
            }
        }, Context.BIND_AUTO_CREATE);
    }

    public static boolean isWatching() {
        if (workService != null)
            return workService.isWorkRunning(intent, flags, startId);
        return false;
    }

    public static void stopDaemon() {
        if (workService != null)
            if (workService.isWorkRunning(intent, flags, startId))
                workService.stopWork(intent, flags, startId);
    }

    public static void onBackPressed(@NonNull Activity a) {
        IntentWrapper.onBackPressed(a);
    }

    public static void addWhiteListMatters(@NonNull Activity activity, String reason) {
        IntentWrapper.whiteListMatters(activity, reason);
    }

    public static boolean isAddedWhiteList(@NonNull Activity activity) {
        return IntentWrapper.isIgnoringBatteryOptimizations(activity);
    }

    /**
     * @deprecated inner call, developer call {@link #startDaemon(Class)} pls.
     * */
    public static void startServiceSafely(Intent i) {
        if (!sInitialized) return;
        try { sApp.get().startService(i); } catch (Exception ignored) {}
    }

    public static int getWakeUpInterval() {
        return Math.max(sWakeUpInterval, MINIMAL_WAKE_UP_INTERVAL);
    }
}
